package v1

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"gitlab.local/backend/proto/dto"
	"manager/auth"
	"manager/bll"
	"manager/client/grpc"
	log "manager/collector/logger"
	"manager/config"
	"manager/errors"
	"manager/model"
	"manager/server/web/middleware"
	"manager/store/postgres"
	"manager/store/redis"
	"manager/utils"
	"os"
	"strconv"
	"strings"
	"time"
)

var Ebike = &ebike{}

func init() {
	// 注册路由
	RegisterRouter(Ebike)
}

type ebike struct{}

func (a *ebike) Init(r *gin.RouterGroup) {
	g := r.Group("/device", middleware.Auth())
	{
		g.POST("/create", a.create)
		g.POST("/update", a.update)
		g.POST("/list", a.list)
		g.POST("/user_list", a.userList) //用户车辆列表
		g.POST("/delete", a.delete)
		g.POST("/info", a.info)
		g.POST("/run_report", a.runReport)
		g.POST("/distribute", a.distribute) // 车辆分发
		g.POST("/change", a.change)         //车辆变动
		g.POST("/import", a.upload)
		g.POST("/bike_info", a.bikeInfo)
		g.POST("/user_bike_info", a.userBikeInfo)
		g.POST("/batch_create", a.batchCreate) // 排量创建
	}
	// 第三方回调接口
	r.POST("/devices/erp/callback", middleware.ApiKeyAuth(), a.ebikeCallback)

	appg := r.Group("/app/device", middleware.Auth())
	{
		appg.POST("/bind", a.bind) // 车辆绑定

		appg.POST("/unbind", a.unbind) // 车辆解除绑定

		appg.POST("/list", a.appList) //用户车辆列表

		appg.POST("/info", a.appInfo) //车辆详情

		appg.POST("/bike_info", a.bikeInfo)
		appg.POST("/user_bike_info", a.userBikeInfo)

		appg.POST("/run_report", a.appRunReport) //app上报信息

		appg.POST("/switch", a.exec) // 车辆启动/关闭

		appg.POST("/find_by_sn", a.findBySn)

		appg.POST("/update", a.update)

		appg.POST("/lock", a.lock)

		appg.POST("/device_status", a.deviceStatus)
		// 授权转移
		appg.POST("/auth_transfer", a.authTransfer)

		appg.POST("/operation_info", a.operationInfo)

		appg.POST("/check_bluetooth", a.checkBluetooth)
		// 检查车辆类型配置是否更新
		appg.POST("/check_func_config", a.checkFuncConfig)
		// 车辆类型功能配置
		appg.POST("/func_config", a.deviceFuncConfig)
	}
}

func (a *ebike) batchCreate(c *gin.Context) {
	var (
		in  = &model.BatchCreateReq{}
		err error
	)

	in.TypeID = utils.StrToInt64(c.PostForm("type_id"))
	in.Color = c.PostForm("color")
	in.Photo = c.PostForm("photo")
	in.BatteryType = utils.StrToInt64(c.PostForm("battery_type"))

	if in.TypeID <= 0 || in.Color == "" || in.Photo == "" || in.BatteryType <= 0 {
		c.Error(errors.ParameterErr.Error())
		return
	}

	var (
		suffixName string
		maxSize    = int64(1024 * 1024 * 100)
		fileName   string
		dst        string
	)

	file, err := c.FormFile("file")
	if err != nil {
		c.Error(errors.FileParsingFailed.New("文件解析失败"))
		return
	}
	// 判断文件名
	suffix := strings.Split(file.Filename, ".")
	if len(suffix) < 1 {
		c.Error(errors.FileNameFormat.New("文件名格式不正确"))
		return
	}
	suffixName = suffix[len(suffix)-1] //后缀名
	fileName = fmt.Sprintf("%s_%s.%s", suffix[0], strconv.FormatInt(time.Now().UnixNano(), 10), suffixName)

	// 判断文件类型 8-2日确认改为只支持csv格式
	_, ok := model.FileMap[suffixName]
	if !ok {
		c.Error(errors.FileTypeErr.New("文件类型不正确"))
		return
	}

	// 判断大小
	if file.Size > maxSize {
		c.Error(errors.FileSizeMax.New("超出文件最大限制 100M"))
		return
	}

	if err = CreateFile("./file"); err != nil {
		c.Error(errors.New("上传失败"))
		return
	}

	//保存路径拼接当前目录下
	dst = fmt.Sprintf("./file/%s", fileName)

	//上传文件到指定的目录
	if err = c.SaveUploadedFile(file, dst); err != nil {
		c.Error(errors.Wrap(err, "上传失败"))
		return
	}

	defer func() { // 删除上传文件
		err = os.Remove(dst)
		if err != nil {
			log.Errorf("delete file err %s", err.Error())
		}
	}()

	// 批量创建
	err, faileList := bll.Ebike.BatchCreate(c.Request.Context(), dst, in)
	if err != nil {
		c.Error(err)
		return
	}

	utils.ResponseOk(c, &model.BatchCreateResp{
		FailedSnCode: faileList,
	})
}

func (a *ebike) create(c *gin.Context) {
	var (
		in  = &model.EbikeCreateRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Create(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

func (a *ebike) ebikeCallback(c *gin.Context) {
	var (
		in  = &model.DeviceCallBackRequest{}
		out model.DeviceCallBackResponse
		err error
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}
	if out, err = bll.Ebike.EbikeCallback(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

func (a *ebike) update(c *gin.Context) {
	var (
		in  = &model.EbikeUpdateRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Update(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

func (a *ebike) list(c *gin.Context) {
	var (
		in  = &model.EbikeListRequest{}
		out = &model.EbikesListResponse{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Ebike.List(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

func (a *ebike) delete(c *gin.Context) {
	var (
		in  = &model.EbikeDeleteRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Delete(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

// bind 车辆绑定
func (a *ebike) bind(c *gin.Context) {
	var (
		in  = &model.EbikeAppBindRequest{}
		err error
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Bind(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, err)

}

func (a *ebike) appInfo(c *gin.Context) {
	var (
		in  = &model.EbikeBaseRequest{}
		out = &model.AppEbikeInfoResponse{}
		err error
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Ebike.AppInfo(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

func (a *ebike) unbind(c *gin.Context) {
	var (
		in  = &model.EbikeBaseRequest{}
		err error
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.UnBind(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, err)
}

func (a *ebike) info(c *gin.Context) {
	var (
		in  = &model.EbikeBaseRequest{}
		err error
		out *model.EbikeInfoResponse
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Ebike.Info(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

func (a *ebike) runReport(c *gin.Context) {

}

func (a *ebike) appRunReport(c *gin.Context) {

}

func (a *ebike) appList(c *gin.Context) {
	var (
		err    error
		out    []*model.EbikeAppListResponse
		userID int64
	)

	// 用户车辆信息
	userID, err = auth.ContextUserID(c.Request.Context())
	if err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Ebike.AppList(c.Request.Context(), userID); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

func (a *ebike) userList(c *gin.Context) {
	var (
		err error
		out []*model.EbikeAppListResponse
		in  model.UserListEbikeRequest
	)

	if err = c.ShouldBindJSON(&in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Ebike.AppList(c.Request.Context(), in.ID); err != nil {
		c.Error(err)
		return
	}

	var newList []*model.EbikeAppListResponse
	for i := 0; i < len(out); i++ {
		if in.FrameCode != nil && in.IsManager != nil {
			if out[i].FrameCode == *in.FrameCode && out[i].IsManager == *in.IsManager {
				newList = append(newList, out[i])
			}
			continue
		}

		if in.FrameCode != nil {
			if out[i].FrameCode == *in.FrameCode {
				newList = append(newList, out[i])
			}
			continue
		}

		if in.IsManager != nil {
			if out[i].IsManager == *in.IsManager {
				newList = append(newList, out[i])
			}
			continue
		}

		newList = append(newList, out[i])
	}

	type ret struct {
		List  []*model.EbikeAppListResponse `json:"list"`
		Total int                           `json:"total"`
	}

	utils.ResponseOk(c, &ret{
		List:  newList,
		Total: len(newList),
	})
}

func (a *ebike) exec(c *gin.Context) {
	var (
		err error
		in  = &model.EbikeAppExecRequest{}
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Exec(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, err)

}

func (a *ebike) findBySn(c *gin.Context) {
	var (
		in  = model.AppEbikeFindBySnRequest{}
		out *model.AppEbikeFindBySnResponse
		err error
	)

	if err := c.ShouldBindJSON(&in); err != nil {
		c.Error(err)
		return
	}

	out, err = bll.Ebike.FindBySn(c.Request.Context(), in.SnCode)
	if err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

// lock 车辆锁定解锁
func (a *ebike) lock(c *gin.Context) {
	var (
		err error
		in  = &model.EbikeAppLockRequest{}
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Lock(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	// 解除锁定

	utils.ResponseOk(c, err)
}

// distribute 车辆分发
func (a *ebike) distribute(c *gin.Context) {
	var (
		err error
		in  = &model.EbikeDistributeRequest{}
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Distribute(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

// change 车辆变动
func (a *ebike) change(c *gin.Context) {
	var (
		err error
		in  = &model.EbikeChangeRequired{}
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Ebike.Change(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

// deviceStatus 设备状态
func (a *ebike) deviceStatus(c *gin.Context) {
	var (
		err error
		in  = &model.DeviceStatusReq{}
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 获取车辆状态
	powerStatus := bll.DeviceStatus.GetPowerStatus(in.EbikeId)

	// 车辆信息
	bikeInfo, _ := bll.CacheBike.GetBikeInfoById(in.EbikeId)
	// 获取设备属性
	statusMap, _ := redis.KV.HGetAll(c, fmt.Sprintf("device_status:%v", bikeInfo.SNCode))

	lockStatus := 1
	onlineStatus := 0
	if len(statusMap) > 0 {
		for k, v := range statusMap {
			if k == "lock" && v == "1" {
				lockStatus = 0
			}

			if k == "online" && v == "1" {
				onlineStatus = 1
			}
		}
	}

	utils.ResponseOk(c, &model.DeviceStatusRsp{
		EbikeId:      in.EbikeId,
		PowerStatus:  powerStatus,
		LockStatus:   lockStatus,
		OnlineStatus: onlineStatus,
	})
}

// authTransfer 授权转移
func (a *ebike) authTransfer(c *gin.Context) {
	var (
		in  = &model.AuthTransferReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 参数校验
	if in.EbikeId == 0 || len(in.TargetPhones) == 0 {
		c.Error(errors.ParameterErr.Error())
		return
	}

	// 手机号格式校验
	for i := 0; i < len(in.TargetPhones); i++ {
		if !utils.VerifyMobileFormat(in.TargetPhones[i]) {
			c.Error(errors.New("手机号格式错误"))
			return
		}
	}

	if err = bll.Ebike.AuthTransfer(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

// operationInfo 运行信息
func (a *ebike) operationInfo(c *gin.Context) {
	var (
		in  = &model.OperationInfoReq{}
		out *model.OperationInfoRsp
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 参数校验
	if in.EbikeId == 0 {
		c.Error(errors.ParameterErr.Error())
		return
	}

	out, err = bll.Ebike.OperationInfo(c.Request.Context(), in)
	if err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

func (a *ebike) upload(c *gin.Context) {
	var (
		suffixName string
		maxSize    = int64(1024 * 1024 * 100)
		fileName   string
		dst        string
	)

	file, err := c.FormFile("file")
	if err != nil {
		c.Error(errors.FileParsingFailed.New("文件解析失败"))
		return
	}
	// 判断文件名
	suffix := strings.Split(file.Filename, ".")
	if len(suffix) < 1 {
		c.Error(errors.FileNameFormat.New("文件名格式不正确"))
		return
	}
	suffixName = suffix[len(suffix)-1] //后缀名
	fileName = fmt.Sprintf("%s_%s.%s", suffix[0], strconv.FormatInt(time.Now().UnixNano(), 10), suffixName)

	// 判断文件类型 8-2日确认改为只支持csv格式
	_, ok := model.FileMap[suffixName]
	if !ok {
		c.Error(errors.FileTypeErr.New("文件类型不正确"))
		return
	}

	// 判断大小
	if file.Size > maxSize {
		c.Error(errors.FileSizeMax.New("超出文件最大限制 100M"))
		return
	}

	if err = CreateFile("./file"); err != nil {
		c.Error(errors.New("上传失败"))
		return
	}

	//保存路径拼接当前目录下
	dst = fmt.Sprintf("./file/%s", fileName)

	//上传文件到指定的目录
	if err = c.SaveUploadedFile(file, dst); err != nil {
		c.Error(errors.Wrap(err, "上传失败"))
		return
	}
	// 上传
	if err = bll.Ebike.Upload(c.Request.Context(), dst); err != nil {
		c.Error(err)
		return
	}
	// 删除上传文件
	err = os.Remove(dst)
	if err != nil {
		log.Errorf("delete file err %s", err.Error())
	}

	utils.ResponseOk(c, nil)
}

// bikeInfo 车辆信息
func (a *ebike) bikeInfo(c *gin.Context) {
	var (
		in  = &model.BikeInfoRequest{}
		err error
		out *model.BikeInfo
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 车辆信息
	if out, err = bll.Ebike.BikeInfo(c.Request.Context(), in.EbikeId); err != nil {
		c.Error(err)
		return
	}

	utils.ResponseOk(c, out)
	return
}

// userBikeInfo 用户车辆数据
func (a *ebike) userBikeInfo(c *gin.Context) {
	var (
		in  = &model.BikeInfoRequest{}
		err error
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 车辆信息
	var bikeInfo *model.BikeInfo
	if bikeInfo, err = bll.Ebike.BikeInfo(c.Request.Context(), in.EbikeId); err != nil {
		c.Error(err)
		return
	}

	// 用户车辆信息
	var userBike *model.UserBikeDetailResponse
	if userBike, err = bll.UserEbike.Detail(c.Request.Context(), &model.UserBikeDetailRequest{EbikeId: in.EbikeId}); err != nil {
		c.Error(err)
		return
	}

	// 电池信息
	batterMap := make(map[int64]*model.BatteryData)
	_, batteryList, _ := postgres.Battery.BatteryInfoList(c, []int64{bikeInfo.BatteryId})
	for i := 0; i < len(batteryList); i++ {
		batterMap[batteryList[i].Id] = batteryList[i]
	}

	if userBike.Name != "" {
		bikeInfo.Name = userBike.Name
	}

	if userBike.Photo != "" {
		bikeInfo.Photo = userBike.Photo
	}

	// 设置状态
	deviceStatus := bll.Ebike.DeviceStatus(c, bikeInfo.ID)

	out := &model.UserBikeInfo{
		DeviceStatusRsp: *deviceStatus,
		BikeInfo:        *bikeInfo,
		BikeName:        userBike.Name,
		BikeImage:       userBike.Photo,
		IsManager:       userBike.IsManager,
		Name:            bikeInfo.Name,
	}

	if v, ok := batterMap[out.BatteryId]; ok {
		out.BatteryType = v.Type
		out.BatteryFirmwaresVersion = v.FirmwaresVersion
	}

	utils.ResponseOk(c, out)
	return
}

// 检查蓝牙
func (a *ebike) checkBluetooth(c *gin.Context) {
	var (
		in  = &model.CheckBluetoothReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 车辆信息
	bikeInfo, err := bll.CacheBike.GetBikeInfoBySn(in.Sn)
	if err != nil {
		c.Error(err)
		return
	}

	ebikeInfo, err := postgres.Ebike.Find(c, bikeInfo.ID)
	if err != nil || ebikeInfo == nil {
		return
	}

	if ebikeInfo.IsBind == 1 {
		c.Error(errors.New("车辆已绑定"))
		return
	}

	// 用户信息
	userId, _ := auth.ContextUserID(c.Request.Context())
	userInfo, err := grpc.UCmd.UserInfo(c, &dto.UserInfoRequest{
		Id: userId,
	})
	if err != nil {
		c.Error(err)
		return
	}

	// 判断是否是白名单
	if config.IsBindWhite(userInfo.Phone) {
		utils.ResponseOk(c, nil)
		return
	}

	for i := 0; i < len(bikeInfo.BindUsers); i++ {
		if bikeInfo.BindUsers[i] == userInfo.Phone {
			utils.ResponseOk(c, nil)
			return
		}
	}

	c.Error(errors.New("用户不在绑定列表中"))
	return
}

// 检查车辆类型配置
func (a *ebike) checkFuncConfig(c *gin.Context) {
	var (
		in           = &model.CheckDeviceConfigRequest{}
		err          error
		ebikeTypeRes *model.EbikeTypeInfo
		isUpdated    int
	)
	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	ebikeTypeReq := &model.EbikeTypeBaseRequest{
		ID: in.DeviceTypeId,
	}
	if ebikeTypeRes, err = bll.EbikeType.Info(c.Request.Context(), ebikeTypeReq); err != nil {
		c.Error(err)
		return
	}
	if ebikeTypeRes.ConfigUpdateAt > in.UpdatedAt {
		isUpdated = 1
	}
	out := &model.CheckDeviceConfigResponse{
		IsUpdated: isUpdated,
	}
	utils.ResponseOk(c, out)
	return
}

// 获取指定车型的功能配置
func (a *ebike) deviceFuncConfig(c *gin.Context) {
	var (
		in           = &model.EbikeTypeBaseRequest{}
		err          error
		ebikeTypeRes *model.EbikeTypeInfo
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if ebikeTypeRes, err = bll.EbikeType.Info(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}

	out := &model.EbikeTypeFuncConfigResponse{
		Config: ebikeTypeRes.Config,
	}
	utils.ResponseOk(c, out)
	return
}

// CreateFile 创建文件
func CreateFile(filePath string) error {
	if !IsExist(filePath) {
		err := os.Mkdir(filePath, os.ModePerm)
		if err != nil {
			log.Error("create upload filepath error %s", filePath)
			return err
		}
		return err
	}
	return nil
}

// IsExist 判断所给路径文件/文件夹是否存在(返回true是存在)
func IsExist(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}
